/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package relationships;

import distribution.RelationshipDistribution;
import java.util.logging.Level;
import java.util.logging.Logger;
import pedigreereconstruction.*;

/**
 *
 * @author arthiramachandran
 */
public class ProbabilisticRelationship extends Relationship {

    RelationshipDistribution relationshipDistribution;
    double likelihood;
    double likelihoodOfNull;

    /*public ProbabilisticRelationship(Individual i1, Individual i2, double total,
    int num, double avg) {
    super(i1, i2, total, num, avg, Integer.MAX_VALUE, Integer.MAX_VALUE, 0); // unrelated
    }
     * 
     */
    public ProbabilisticRelationship(Individual i1, Individual i2, double total,
            int num, double avg, RelationshipTypes rt) {
        super(i1, i2, total, num, avg, rt.meiosesUp, rt.meiosesDown, rt.founders); // unrelated
    }

    /*
    public ProbabilisticRelationship(Individual i1, Individual i2, double total,
    int num) {
    super(i1, i2, total, num, total / num, Integer.MAX_VALUE, Integer.MAX_VALUE, 0); // unrelated
    }
     * 
     */
    public ProbabilisticRelationship(Individual i1, Individual i2, double total,
            int num, RelationshipTypes rt) {
        super(i1, i2, total, num, total / num, rt.meiosesUp, rt.meiosesDown, rt.founders); // unrelated
    }

    public ProbabilisticRelationship(Relationship r) {
        super(r.individual1, r.individual2, r.totalSharedIBD,
                r.numberOfSharedSegments, r.averageSharedIBD,
                r.relationshipType.meiosesUp, r.relationshipType.meiosesDown, r.relationshipType.founders);
    }

    public ProbabilisticRelationship(Relationship r, RelationshipTypes rt) {
        super(r.individual1, r.individual2, r.totalSharedIBD,
                r.numberOfSharedSegments, r.averageSharedIBD,
                rt.meiosesUp, rt.meiosesDown, rt.founders);
    }
    /*
    @Override
    public void setIndividuals(Individual i1, Individual i2) {
    throw new UnsupportedOperationException("Not supported yet.");
    }
    
    @Override
    public void setSharing(Integer totalSharedIBD, Integer numberOfSharedSegments) {
    throw new UnsupportedOperationException("Not supported yet.");
    }
    
    @Override
    public Boolean isIndividualInRelationship(Individual i) {
    throw new UnsupportedOperationException("Not supported yet.");
    }
     * /
     * 
     */

    public double calculateNullLikelihood() {
        if (this.totalSharedIBD == -1 || this.numberOfSharedSegments == -1) {
            return 1;
        }
        ProbabilisticRelationship nullR = new ProbabilisticRelationship(
                this.individual1, this.individual2, this.totalSharedIBD,
                this.numberOfSharedSegments, PedigreeReconstruction.unrelated);
        nullR.setRelationshipDistribution(
                PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.unrelated));
        try {
            nullR.calculateRelationshipLikelihood();
            if (!this.isRelationshipSymmetric()) // account for both sides of an unrelated relationship
            {
                nullR.setRelationshipLikelihood(nullR.getRelationshipLikelihood() * nullR.getRelationshipLikelihood());
            }

        } catch (Exception ex) {
            Logger.getLogger(ProbabilisticRelationship.class.getName()).log(Level.SEVERE, null, ex);
        }
        // System.out.println("### " + nullR.toVerboseString());
        this.likelihoodOfNull = nullR.likelihood;
        return nullR.likelihood;
    }

    public void calculateRelationshipLikelihood() throws Exception {
        // TODO: need to clean up what happens if we don't have information
        /*if (this.relationshipType.equals(PedigreeReconstruction.grandparent_grandchild)) {
        System.out.println("NEED TO REMOVE");
        System.out.println(this);
        this.likelihood = 0;
        } else 
         */
        if (this.totalSharedIBD == -1 || this.numberOfSharedSegments == -1) {
            this.likelihood = 1;
        } else {
            //System.out.println(this.numberOfSharedSegments + "\t " + this.totalSharedIBD);
            double temp = 0;
            try {
                //System.out.println(this);
                long startTime = System.nanoTime();
                temp = this.relationshipDistribution.calculateCDF(
                        this.totalSharedIBD, this.numberOfSharedSegments);
                long endTime = System.nanoTime();
                //System.out.println("time: " + (endTime - startTime));
            } catch (Exception e) {
                System.out.println("####");
                System.out.println(this + "---\n" + e);
                /*
                System.out.println(this.relationshipDistribution.lengthDistribution.getBinStats());
                System.out.println(this.relationshipDistribution.countDistribution.getBinStats());
                throw new Exception(this
                + " " + this.relationshipDistribution.lengthDistribution.cumulativeProbability(this.totalSharedIBD)
                + " " + this.relationshipDistribution.countDistribution.cumulativeProbability(this.numberOfSharedSegments)
                + " " + "---\n" + e);
                 * 
                 */
            }
            if (temp <= 0) {
                System.out.println(temp);
                System.out.println("!!!!!" + this.relationshipDistribution.detailedCalculateCdf(this.totalSharedIBD, this.numberOfSharedSegments));
                temp = 10e-10; // TODO: ARVITRARY!!
            }
            // Accounting for both edges of a symmetric relationship 
            // being present in the graph
            if (this.isRelationshipSymmetric()) {
                this.likelihood = Math.sqrt(temp);
            } else {
                this.likelihood = temp;
            }
            if (this.likelihood > 1) {
                throw new Exception(
                        "Probability should not be > 1; probability = " + this.likelihood);
            }
            if (this.likelihood == 1
                    && this.totalSharedIBD < 2000
                    && !this.relationshipTypeEquals("UNRELATED")) {
                System.out.println("ahh!!!" + this);
                /*
                System.out.println(this + " random: "
                + " " + this.relationshipDistribution.lengthDistribution.cdf((int) (this.totalSharedIBD * .1))
                + " " + this.totalSharedIBD * .1
                + "-- " + this.relationshipDistribution.lengthDistribution.nextInt() + " --"
                + " " + this.relationshipDistribution.countDistribution.cdf((int) (this.numberOfSharedSegments * .85))
                + " " + this.numberOfSharedSegments * .85
                + "-- " + this.relationshipDistribution.countDistribution.nextInt() + " --");
                System.out.println("Checking weird thing: " 
                + this.relationshipDistribution.countDistribution.cdf((int)(this.totalSharedIBD*.1)) + " "
                + this.relationshipDistribution.lengthDistribution.cdf((int)(this.totalSharedIBD*.1)));
                //System.out.println("lengths: " + this.relationshipDistribution.lengthDistribution);
                RelationshipDistribution rd =
                PedigreeReconstruction.knownPairwiseDistributions.get(
                PedigreeReconstruction.unrelated);
                System.out.println(rd.calculateCDF(
                this.totalSharedIBD, this.numberOfSharedSegments)
                + " " + rd.lengthDistribution.cdf((int) (this.totalSharedIBD * .1))
                + " " + this.totalSharedIBD * .1 + "-- " + rd.lengthDistribution.nextDouble() + " --"
                + " " + rd.countDistribution.cdf((int) (this.numberOfSharedSegments * .85))
                + " " + this.numberOfSharedSegments * .85 + "-- " + rd.countDistribution.nextInt() + " --");
                 * 
                 */

            }
        }
    }

    public void setRelationshipLikelihood(double l) {
        this.likelihood = l;
    }

    public double getRelationshipLikelihood() {
        return this.likelihood;
    }

    public void setRelationshipDistribution(RelationshipDistribution dist) {
        this.relationshipDistribution = dist;
    }

    public boolean isRelationshipSymmetric() {
        return this.relationshipDistribution.symmetric;
    }

    public boolean relationshipTypeEquals(String type) {
        return this.relationshipDistribution.getName().equals(type);
    }

    /*
    @Override
    public boolean equals(Object obj) {
    if (obj instanceof ProbabilisticRelationship) {
    ProbabilisticRelationship r = (ProbabilisticRelationship) obj;
    return (r.individual1.equals(individual1)
    && r.individual2.equals(individual2)
    && r.relationshipDistribution.equals(
    relationshipDistribution));
    
    }
    return false;
    
    }
     * 
     */

    /*
    public boolean equals(Object obj) {
    if (obj instanceof ProbabilisticRelationship) {
    ProbabilisticRelationship r = (ProbabilisticRelationship) obj;
    return (r.individual1.equals(individual1)
    && r.individual2.equals(individual2)
    && r.totalSharedIBD == totalSharedIBD
    && r.numberOfSharedSegments == numberOfSharedSegments
    && r.averageSharedIBD == averageSharedIBD
    && r.relationshipDistribution.equals(
    relationshipDistribution));
    
    }
    return false;
    
    }
     * 
     */
    /*
    @Override
    public int hashCode() {
    int hash = 7;
    hash =
    31 * hash + (this.relationshipDistribution != null ? this.relationshipDistribution.hashCode() : 0);
    return hash;
    }
     * 
     */
    @Override
    public boolean equals(Object obj) {
        //System.out.println("!!!");
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ProbabilisticRelationship other = (ProbabilisticRelationship) obj;
        /*if (!this.relationshipDistribution.equals(other.relationshipDistribution)
        && (this.relationshipDistribution == null
        || !this.relationshipDistribution.equals(other.relationshipDistribution))) {
        return false;
        }
         * 
         */
        /*
        if (this.relationshipDistribution==null)
        System.out.println("Not intitialized: " + this);
        if (other.relationshipDistribution==null)
        System.out.println("Not intitialized: " + other);
         * 
         */
        if (!this.relationshipDistribution.equals(other.relationshipDistribution)) {
            return false;
        }
        /*if (this.isRelationshipSymmetric()) {
        return ((this.individual1.id.equals(individual1.id)
        && this.individual2.id.equals(individual2.id))
        || (this.individual1.id.equals(individual2.id)
        && this.individual2.id.equals(individual1.id)));
        } else 
         */
        // specific case where the parent is missing in one of the relationships 
        // - maybe one relationship can be used to fill in the blanks
        /*if (this.relationshipType.equals(PedigreeReconstruction.parent_child)) {
        if (this.individual1.isMissing() || other.individual1.isMissing()) {
        return this.individual2.equals(other.individual2);
        }
        }
         * 
         */

        //System.out.println(this + "\t" + other);
        //System.out.println(this.individual1.id + " " + individual1.id + " " + 
        //        this.individual2.id + " " + individual2.id);
        return (this.individual1.equals(other.individual1)
                && this.individual2.equals(other.individual2));
    }

    @Override
    public boolean equalsIgnoreMissingParent(Object obj) {
        //zSystem.out.println("!!!");
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ProbabilisticRelationship other = (ProbabilisticRelationship) obj;
        if (this.relationshipDistribution != other.relationshipDistribution
                && (this.relationshipDistribution == null
                || !this.relationshipDistribution.equals(other.relationshipDistribution))) {
            return false;
        }

        // specific case where the parent is missing in one of the relationships 
        // - maybe one relationship can be used to fill in the blanks
        if (this.relationshipType.equals(PedigreeReconstruction.parent_child)) {
            if (this.individual1.isMissing() || other.individual1.isMissing()) {
                return this.individual2.equals(other.individual2);
            }
        }


        return (this.individual1.equals(other.individual1)
                && this.individual2.equals(other.individual2));
    }

    public double getRandomRelationshipProbability() {
        return this.relationshipDistribution.getRandomRelationshipProbability();
    }

    @Override
    public String toString() {
        if (this.relationshipDistribution == null) {
            return "<" + individual1 + "-" + individual2 + "(" + "_"
                    + this.relationshipType + ","
                    + this.likelihood + "(c="
                    + this.numberOfSharedSegments + ",l="
                    + this.totalSharedIBD + ")>";
        } else {
            return "<" + individual1 + "-" + individual2 + "("
                    + this.relationshipDistribution.getName() + "_" + this.relationshipType + ","
                    + this.likelihood + "(c="
                    + this.numberOfSharedSegments + ",l="
                    + this.totalSharedIBD + ")>";
        }
        /*
        if (this.relationshipDistribution == null) {
        return "<" + individual1 + "-" + individual2 + "(" + "_" + this.relationshipType + "," + this.likelihood + ")>";
        } else {
        return "<" + individual1 + "-" + individual2 + "(" + this.relationshipDistribution.getName() + "_" + this.relationshipType + "," + this.likelihood + ")>";
        }
         * 
         */
        //return "<" + individual1 + "," + individual2 + "(" + totalSharedIBD + "," + numberOfSharedSegments + "," + averageSharedIBD + ")>";
    }

    public RelationshipDistribution getRelationshipDistribution() {
        return this.relationshipDistribution;
    }

    public boolean containsMissing() {
        return (this.individual1.isMissing() || this.individual2.isMissing());
    }

    public Individual getNonMissingIndividual() {
        if (this.individual1.isMissing()) {
            return this.individual2;
        } else if (this.individual2.isMissing()) {
            return this.individual1;
        } else {
            return null;
        }
    }

    public String toVerboseString() {
        if (this.relationshipDistribution == null) {
            return "<" + individual1 + "-" + individual2 + "(" + "_"
                    + this.relationshipType + ","
                    + this.likelihood + "(c="
                    + this.numberOfSharedSegments + ",l="
                    + this.totalSharedIBD + ")>";
        } else {
            return "<" + individual1 + "-" + individual2 + "("
                    + this.relationshipDistribution.getName() + "_" + this.relationshipType + ","
                    + this.likelihood + "(c="
                    + this.numberOfSharedSegments + ",l="
                    + this.totalSharedIBD + ")>";
        }
        //return "<" + individual1 + "," + individual2 + "(" + totalSharedIBD + "," + numberOfSharedSegments + "," + averageSharedIBD + ")>";
    }
}
